package com.apes.framework.util.statistics;

import com.alibaba.fastjson.JSONObject;
import com.apes.framework.api.SpringManager;
import com.apes.framework.config.elasticsearch.ApesElasticsearchHealthIndicator;
import com.apes.framework.jpa.ApesBean;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.plugin.event.Listener;
import com.apes.framework.rop.session.SimpleSession;
import com.apes.framework.util.*;
import com.apes.oap.session.Session;
import lombok.SneakyThrows;
import org.elasticsearch.client.ElasticsearchClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.reindex.DeleteByQueryAction;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.health.Status;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

@Service
public class StatisticsService {

    @Autowired
    private ElasticsearchClient client;

    @Autowired
    private StatisticsRepository repository;

    private ApesElasticsearchHealthIndicator healthIndicator;

    @Value("${rocketmq.tags:}")
    private String tags;

    @Value("${logger.inner.invoke.filters:}")
    private String filters;

    public StatisticsService(ApesElasticsearchHealthIndicator healthIndicator) {
        this.healthIndicator = healthIndicator;
    }

    private boolean filter(String method) {
        if (StringUtils.isEmpty(this.filters)) return false;
        String[] filters = this.filters.split(",");
        for (String filter : filters) {
            if (method.indexOf(filter.trim()) != -1) return true;
        }
        return false;
    }

    @Listener(topic = "event:log.message.save")
    public void consume(SimpleRequest request) {
        Statistics statistics = request.getO(Statistics.class);
        statistics.setEndOnTimeStamp(System.currentTimeMillis());
        statistics.setTime(statistics.getEndOnTimeStamp() - statistics.getCreatedOnTimeStamp());
        statistics.setEndTime(DateUtil.timeStampToStr(statistics.getEndOnTimeStamp()));
        boolean health = healthIndicator != null ? healthIndicator.health().getStatus() == Status.UP : false;
        if (health) {
            repository.save(statistics);
        } else {
            EventUtil.sendSynEvent(this, "event:log.message.publish", statistics);
        }
    }

    public Statistics createStatistics(SimpleRequest request) {
        Statistics statistics = null;
        try {
            String method = request.getRopRequestContext().getMethod();
            if (!filter(method)) {
                return statistics;
            }
            statistics = new Statistics();
            statistics.setServer(NetUtil.getServerIp());
            statistics.setMethod(method);
            statistics.setVersion(request.getRopRequestContext().getVersion());
            statistics.setAppKey(request.getRopRequestContext().getAppKey());
            statistics.setSessionId(request.getRopRequestContext().getSessionId());
            statistics.setIp(request.getRopRequestContext().getIp());
            statistics.setRequest(toJSONString(request.getData()));
            Session session = request.getRopRequestContext().getSession();
            if (session != null) {
                statistics.setUserId(((SimpleSession) session).getUserId());
                statistics.setPostId(((SimpleSession) session).getPostId());
            }
            statistics.setTags(tags);
            statistics.setInvokeMethod("esb.inner.invoke");
            statistics.setCreatedOnTimeStamp(System.currentTimeMillis());
            statistics.setBeginTime(DateUtil.timeStampToStr(statistics.getCreatedOnTimeStamp()));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return statistics;
    }

    public String toJSONString(Object context) {
        String result = null;
        try {
            if (context instanceof ApesBean) {
                result = JSONObject.toJSONString(MapUtil.mapped(context));
            } else {
                result = JSONObject.toJSONString(context);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 获取队列消息
     *
     * @param request
     * @return
     */
    public Page<Statistics> findByMethod(SimpleRequest request) {
        List<Map<String, String>> condition = request.get("condition");
        PageRequest pageRequest = PageRequest.of(request.get("page"), request.get("size"));
        if (condition.isEmpty()) return SpringManager.getBean(StatisticsRepository.class).findAll(pageRequest);

        Map<String, String> map = new HashMap<>();
        condition.forEach(c -> {
            if (map.containsKey(c.get("field"))) {
                map.put("end", c.get("value"));
            } else {
                map.put(c.get("field"), c.get("value"));
            }
        });
        Page<Statistics> statisticsList = repository.findByMethodAndCreatedOnBetweenOrderByEndTimeDesc(map.get("method"), map.get("created"), map.get("end"), pageRequest);
        return statisticsList;
    }


    /**
     * 清除过期日志
     **/
    @SneakyThrows
    public void clearOutdatedLogs(SimpleRequest request) {
        int expire = request.get("expire");  /**会话有效时长，单位：天**/
        Class model = Class.forName(request.get("model"));
        clearOutdated(expire, model, request.get("field"));
    }

    /**
     * 清除过期数据
     **/
    private void clearOutdated(int expire, Class model, String field) {
        String expireDate = DateUtil.format(DateUtil.add(new Date(), Calendar.DATE, -1 * expire), "yyyy-MM-dd'T'HH:mm:ss.SSS") + "+0800";
        String index = ReflectUtil.getAnnotation(model, Document.class).indexName();
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        queryBuilder.must(QueryBuilders.rangeQuery(field).lte(expireDate));
        DeleteByQueryAction.INSTANCE.newRequestBuilder(client).filter(queryBuilder).source(index).get().getDeleted();
    }
}
